// Copyright (C), 2012-2018, Sichuan Xiaoka Technology Co., Ltd.
package crypto;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;
import java.util.Arrays;

/**
 * AES加解密工具类
 *
 * @author 张鹏
 * @since 1.0
 */
@Slf4j
public final class AesUtils {
    /**
     * 加密算法
     */
    public static final String KEY_ALGORITHM = "AES";
    /**
     * 加密PADDING模式
     */
    public static final String KEY_ALGORITHM_PADDING = "AES/CBC/PKCS7Padding";

    /**
     * 获取SecretKey
     *
     * @param seed 随机种子
     * @return SecretKey
     * @throws Exception
     */
    public static SecretKey getSecretKey(byte[] seed) throws Exception {
        SecureRandom secureRandom = new SecureRandom(seed);
        KeyGenerator keyGenerator = KeyGenerator.getInstance(KEY_ALGORITHM);
        keyGenerator.init(secureRandom);
        return keyGenerator.generateKey();
    }

    /**
     * 加密
     *
     * @param data       要加密的数据
     * @param encryptKey 加密的key
     * @param iv         向量
     * @return 加密后的数据
     */
    public static byte[] encrypt(byte[] data, byte[] encryptKey, byte[] iv) {
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(encryptKey, KEY_ALGORITHM);
        return encrypt(data, zeroIv, key);
    }

    /**
     * 解密
     *
     * @param data       要解密的数据
     * @param decryptKey 解密的key
     * @param iv         向量
     * @return 解密后的数据
     */
    public static byte[] decrypt(byte[] data, byte[] decryptKey, byte[] iv) {
        IvParameterSpec zeroIv = new IvParameterSpec(iv);
        SecretKeySpec key = new SecretKeySpec(decryptKey, KEY_ALGORITHM);
        return decrypt(data, zeroIv, key);
    }

    public static byte[] encrypt(byte[] data, IvParameterSpec zeroIv, SecretKeySpec keySpec) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128);
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, zeroIv);
            return cipher.doFinal(data);
        } catch (Exception e) {
            log.error("AES encrypt ex, iv={}, key={}",
                    Arrays.toString(zeroIv.getIV()),
                    Arrays.toString(keySpec.getEncoded()), e);
            throw new CryptoException("AES encrypt ex", e);
        }
    }

    public static byte[] decrypt(byte[] data, IvParameterSpec zeroIv, SecretKeySpec keySpec) {
        try {
            Cipher cipher = Cipher.getInstance(KEY_ALGORITHM_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, keySpec, zeroIv);
            return cipher.doFinal(data);
        } catch (Exception e) {
            log.error("AES decrypt ex, iv={}, key={}",
                    Arrays.toString(zeroIv.getIV()),
                    Arrays.toString(keySpec.getEncoded()), e);
            throw new CryptoException("AES decrypt ex", e);
        }
    }
}